1   /*
2    * Javassist, a Java-bytecode translator toolkit.
3    * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4    *
5    * The contents of this file are subject to the Mozilla Public License Version
6    * 1.1 (the "License"); you may not use this file except in compliance with
7    * the License.  Alternatively, the contents of this file may be used under
8    * the terms of the GNU Lesser General Public License Version 2.1 or later,
9    * or the Apache License Version 2.0.
10   *
11   * Software distributed under the License is distributed on an "AS IS" basis,
12   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13   * for the specific language governing rights and limitations under the
14   * License.
15   */
16  
17  package scouter.javassist;
18  
19  import java.io.BufferedInputStream;
20  import java.io.ByteArrayInputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.DataInputStream;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.lang.ref.WeakReference;
27  import java.net.URL;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.Hashtable;
31  import java.util.List;
32  import java.util.Set;
33  
34  import scouter.javassist.CannotCompileException;
35  import scouter.javassist.ClassMap;
36  import scouter.javassist.ClassPool;
37  import scouter.javassist.CodeConverter;
38  import scouter.javassist.CtBehavior;
39  import scouter.javassist.CtClass;
40  import scouter.javassist.CtConstructor;
41  import scouter.javassist.CtField;
42  import scouter.javassist.CtMember;
43  import scouter.javassist.CtMethod;
44  import scouter.javassist.Modifier;
45  import scouter.javassist.NotFoundException;
46  import scouter.javassist.bytecode.AccessFlag;
47  import scouter.javassist.bytecode.AnnotationsAttribute;
48  import scouter.javassist.bytecode.AttributeInfo;
49  import scouter.javassist.bytecode.BadBytecode;
50  import scouter.javassist.bytecode.Bytecode;
51  import scouter.javassist.bytecode.ClassFile;
52  import scouter.javassist.bytecode.CodeAttribute;
53  import scouter.javassist.bytecode.CodeIterator;
54  import scouter.javassist.bytecode.ConstPool;
55  import scouter.javassist.bytecode.ConstantAttribute;
56  import scouter.javassist.bytecode.Descriptor;
57  import scouter.javassist.bytecode.EnclosingMethodAttribute;
58  import scouter.javassist.bytecode.FieldInfo;
59  import scouter.javassist.bytecode.InnerClassesAttribute;
60  import scouter.javassist.bytecode.MethodInfo;
61  import scouter.javassist.bytecode.ParameterAnnotationsAttribute;
62  import scouter.javassist.bytecode.SignatureAttribute;
63  import scouter.javassist.bytecode.annotation.Annotation;
64  import scouter.javassist.compiler.AccessorMaker;
65  import scouter.javassist.compiler.CompileError;
66  import scouter.javassist.compiler.Javac;
67  import scouter.javassist.expr.ExprEditor;
68  
69  
70  /**
71   * Class types.
72   */
73  class CtClassType extends CtClass {
74      ClassPool classPool;
75      boolean wasChanged;
76      private boolean wasFrozen;
77      boolean wasPruned;
78      boolean gcConstPool;    // if true, the constant pool entries will be garbage collected. 
79      ClassFile classfile;
80      byte[] rawClassfile;    // backup storage
81  
82      private WeakReference memberCache;
83      private AccessorMaker accessors;
84  
85      private FieldInitLink fieldInitializers;
86      private Hashtable hiddenMethods;    // must be synchronous
87      private int uniqueNumberSeed;
88  
89      private boolean doPruning = ClassPool.doPruning;
90      private int getCount;
91      private static final int GET_THRESHOLD = 2;     // see compress()
92  
93      CtClassType(String name, ClassPool cp) {
94          super(name);
95          classPool = cp;
96          wasChanged = wasFrozen = wasPruned = gcConstPool = false;
97          classfile = null;
98          rawClassfile = null;
99          memberCache = null;
100         accessors = null;
101         fieldInitializers = null;
102         hiddenMethods = null;
103         uniqueNumberSeed = 0;
104         getCount = 0;
105     }
106 
107     CtClassType(InputStream ins, ClassPool cp) throws IOException {
108         this((String)null, cp);
109         classfile = new ClassFile(new DataInputStream(ins));
110         qualifiedName = classfile.getName();
111     }
112 
113     CtClassType(ClassFile cf, ClassPool cp) {
114         this((String)null, cp);
115         classfile = cf;
116         qualifiedName = classfile.getName();
117     }
118 
119     protected void extendToString(StringBuffer buffer) {
120         if (wasChanged)
121             buffer.append("changed ");
122 
123         if (wasFrozen)
124             buffer.append("frozen ");
125 
126         if (wasPruned)
127             buffer.append("pruned ");
128 
129         buffer.append(Modifier.toString(getModifiers()));
130         buffer.append(" class ");
131         buffer.append(getName());
132 
133         try {
134             CtClass ext = getSuperclass();
135             if (ext != null) {
136                 String name = ext.getName();
137                 if (!name.equals("java.lang.Object"))
138                     buffer.append(" extends " + ext.getName());
139             }
140         }
141         catch (NotFoundException e) {
142             buffer.append(" extends ??");
143         }
144 
145         try {
146             CtClass[] intf = getInterfaces();
147             if (intf.length > 0)
148                 buffer.append(" implements ");
149 
150             for (int i = 0; i < intf.length; ++i) {
151                 buffer.append(intf[i].getName());
152                 buffer.append(", ");
153             }
154         }
155         catch (NotFoundException e) {
156             buffer.append(" extends ??");
157         }
158 
159         CtMember.Cache memCache = getMembers();
160         exToString(buffer, " fields=",
161                 memCache.fieldHead(), memCache.lastField());
162         exToString(buffer, " constructors=",
163                 memCache.consHead(), memCache.lastCons());
164         exToString(buffer, " methods=",
165                    memCache.methodHead(), memCache.lastMethod());
166     }
167 
168     private void exToString(StringBuffer buffer, String msg,
169                             CtMember head, CtMember tail) {
170         buffer.append(msg);
171         while (head != tail) {
172             head = head.next();
173             buffer.append(head);
174             buffer.append(", ");
175         }
176     }
177 
178     public AccessorMaker getAccessorMaker() {
179         if (accessors == null)
180             accessors = new AccessorMaker(this);
181 
182         return accessors;
183     }
184 
185     public ClassFile getClassFile2() {
186         ClassFile cfile = classfile;
187         if (cfile != null)
188             return cfile;
189 
190         classPool.compress();
191         if (rawClassfile != null) {
192             try {
193                 classfile = new ClassFile(new DataInputStream(
194                                             new ByteArrayInputStream(rawClassfile)));
195                 rawClassfile = null;
196                 getCount = GET_THRESHOLD;
197                 return classfile;
198             }
199             catch (IOException e) {
200                 throw new RuntimeException(e.toString(), e);
201             }
202         }
203 
204         InputStream fin = null;
205         try {
206             fin = classPool.openClassfile(getName());
207             if (fin == null)
208                 throw new NotFoundException(getName());
209 
210             fin = new BufferedInputStream(fin);
211             ClassFile cf = new ClassFile(new DataInputStream(fin));
212             if (!cf.getName().equals(qualifiedName))
213                 throw new RuntimeException("cannot find " + qualifiedName + ": " 
214                         + cf.getName() + " found in "
215                         + qualifiedName.replace('.', '/') + ".class");
216 
217             classfile = cf;
218             return cf;
219         }
220         catch (NotFoundException e) {
221             throw new RuntimeException(e.toString(), e);
222         }
223         catch (IOException e) {
224             throw new RuntimeException(e.toString(), e);
225         }
226         finally {
227             if (fin != null)
228                 try {
229                     fin.close();
230                 }
231                 catch (IOException e) {}
232         }
233     }
234 
235    /* Inherited from CtClass.  Called by get() in ClassPool.
236     *
237     * @see javassist.CtClass#incGetCounter()
238     * @see #toBytecode(DataOutputStream)
239     */
240    final void incGetCounter() { ++getCount; }
241 
242    /**
243     * Invoked from ClassPool#compress().
244     * It releases the class files that have not been recently used
245     * if they are unmodified. 
246     */
247    void compress() {
248        if (getCount < GET_THRESHOLD)
249            if (!isModified() && ClassPool.releaseUnmodifiedClassFile)
250                removeClassFile();
251            else if (isFrozen() && !wasPruned)
252                saveClassFile();
253 
254        getCount = 0;
255    }
256 
257    /**
258      * Converts a ClassFile object into a byte array
259      * for saving memory space.
260      */
261     private synchronized void saveClassFile() {
262         /* getMembers() and releaseClassFile() are also synchronized.
263          */
264         if (classfile == null || hasMemberCache() != null)
265             return;
266 
267         ByteArrayOutputStream barray = new ByteArrayOutputStream();
268         DataOutputStream out = new DataOutputStream(barray);
269         try {
270             classfile.write(out);
271             barray.close();
272             rawClassfile = barray.toByteArray();
273             classfile = null;
274         }
275         catch (IOException e) {}
276     }
277 
278     private synchronized void removeClassFile() {
279         if (classfile != null && !isModified() && hasMemberCache() == null)
280             classfile = null;
281     }
282 
283     public ClassPool getClassPool() { return classPool; }
284 
285     void setClassPool(ClassPool cp) { classPool = cp; }
286 
287     public URL getURL() throws NotFoundException {
288         URL url = classPool.find(getName());
289         if (url == null)
290             throw new NotFoundException(getName());
291         else
292             return url;
293     }
294 
295     public boolean isModified() { return wasChanged; }
296 
297     public boolean isFrozen() { return wasFrozen; }
298 
299     public void freeze() { wasFrozen = true; }
300 
301     void checkModify() throws RuntimeException {
302         if (isFrozen()) {
303             String msg = getName() + " class is frozen";
304             if (wasPruned)
305                 msg += " and pruned";
306 
307             throw new RuntimeException(msg);
308         }
309 
310         wasChanged = true;
311     }
312 
313     public void defrost() {
314         checkPruned("defrost");
315         wasFrozen = false;
316     }
317 
318     public boolean subtypeOf(CtClass clazz) throws NotFoundException {
319         int i;
320         String cname = clazz.getName();
321         if (this == clazz || getName().equals(cname))
322             return true;
323 
324         ClassFile file = getClassFile2();
325         String supername = file.getSuperclass();
326         if (supername != null && supername.equals(cname))
327             return true;
328 
329         String[] ifs = file.getInterfaces();
330         int num = ifs.length;
331         for (i = 0; i < num; ++i)
332             if (ifs[i].equals(cname))
333                 return true;
334 
335         if (supername != null && classPool.get(supername).subtypeOf(clazz))
336             return true;
337 
338         for (i = 0; i < num; ++i)
339             if (classPool.get(ifs[i]).subtypeOf(clazz))
340                 return true;
341 
342         return false;
343     }
344 
345     public void setName(String name) throws RuntimeException {
346         String oldname = getName();
347         if (name.equals(oldname))
348             return;
349 
350         // check this in advance although classNameChanged() below does.
351         classPool.checkNotFrozen(name);
352         ClassFile cf = getClassFile2();
353         super.setName(name);
354         cf.setName(name);
355         nameReplaced();
356         classPool.classNameChanged(oldname, this);
357     }
358 
359     public String getGenericSignature() {
360         SignatureAttribute sa
361             = (SignatureAttribute)getClassFile2().getAttribute(SignatureAttribute.tag);
362         return sa == null ? null : sa.getSignature();
363     }
364 
365     public void setGenericSignature(String sig) {
366         ClassFile cf = getClassFile();
367         SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig);
368         cf.addAttribute(sa);
369     }
370 
371     public void replaceClassName(ClassMap classnames)
372         throws RuntimeException
373     {
374         String oldClassName = getName();
375         String newClassName
376             = (String)classnames.get(Descriptor.toJvmName(oldClassName));
377         if (newClassName != null) {
378             newClassName = Descriptor.toJavaName(newClassName);
379             // check this in advance although classNameChanged() below does.
380             classPool.checkNotFrozen(newClassName);
381         }
382 
383         super.replaceClassName(classnames);
384         ClassFile cf = getClassFile2();
385         cf.renameClass(classnames);
386         nameReplaced();
387 
388         if (newClassName != null) {
389             super.setName(newClassName);
390             classPool.classNameChanged(oldClassName, this);
391         }
392     }
393 
394     public void replaceClassName(String oldname, String newname)
395         throws RuntimeException
396     {
397         String thisname = getName();
398         if (thisname.equals(oldname))
399             setName(newname);
400         else {
401             super.replaceClassName(oldname, newname);
402             getClassFile2().renameClass(oldname, newname);
403             nameReplaced();
404         }
405     }
406 
407     public boolean isInterface() {
408         return Modifier.isInterface(getModifiers());
409     }
410 
411     public boolean isAnnotation() {
412         return Modifier.isAnnotation(getModifiers());
413     }
414 
415     public boolean isEnum() {
416        return Modifier.isEnum(getModifiers());
417     }
418 
419     public int getModifiers() {
420         ClassFile cf = getClassFile2();
421         int acc = cf.getAccessFlags();
422         acc = AccessFlag.clear(acc, AccessFlag.SUPER);
423         int inner = cf.getInnerAccessFlags();
424         if (inner != -1 && (inner & AccessFlag.STATIC) != 0)
425             acc |= AccessFlag.STATIC;
426 
427         return AccessFlag.toModifier(acc);
428     }
429 
430     public CtClass[] getNestedClasses() throws NotFoundException {
431         ClassFile cf = getClassFile2();
432         InnerClassesAttribute ica
433             = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag);
434         if (ica == null)
435             return new CtClass[0];
436 
437         String thisName = cf.getName() + "$";
438         int n = ica.tableLength();
439         ArrayList list = new ArrayList(n);
440         for (int i = 0; i < n; i++) {
441             String name = ica.innerClass(i);
442             if (name != null)
443                 if (name.startsWith(thisName)) {
444                     // if it is an immediate nested class
445                     if (name.lastIndexOf('$') < thisName.length())
446                         list.add(classPool.get(name));
447                 }
448         }
449 
450         return (CtClass[])list.toArray(new CtClass[list.size()]);
451     }
452 
453     public void setModifiers(int mod) {
454         ClassFile cf = getClassFile2();
455         if (Modifier.isStatic(mod)) {
456             int flags = cf.getInnerAccessFlags();
457             if (flags != -1 && (flags & AccessFlag.STATIC) != 0)
458                 mod = mod & ~Modifier.STATIC;
459             else
460                 throw new RuntimeException("cannot change " + getName() + " into a static class");
461         }
462 
463         checkModify();
464         cf.setAccessFlags(AccessFlag.of(mod));
465     }
466 
467     //@Override
468     public boolean hasAnnotation(String annotationName) {
469         ClassFile cf = getClassFile2();
470         AnnotationsAttribute ainfo = (AnnotationsAttribute)
471                 cf.getAttribute(AnnotationsAttribute.invisibleTag);
472         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
473                 cf.getAttribute(AnnotationsAttribute.visibleTag);
474         return hasAnnotationType(annotationName, getClassPool(), ainfo, ainfo2);
475     }
476 
477     /**
478      * @deprecated
479      */
480     static boolean hasAnnotationType(Class clz, ClassPool cp,
481                                      AnnotationsAttribute a1,
482                                      AnnotationsAttribute a2)
483     {
484         return hasAnnotationType(clz.getName(), cp, a1, a2);
485     }
486 
487     static boolean hasAnnotationType(String annotationTypeName, ClassPool cp,
488                                      AnnotationsAttribute a1,
489                                      AnnotationsAttribute a2)
490     {
491         Annotation[] anno1, anno2;
492 
493         if (a1 == null)
494             anno1 = null;
495         else
496             anno1 = a1.getAnnotations();
497 
498         if (a2 == null)
499             anno2 = null;
500         else
501             anno2 = a2.getAnnotations();
502 
503         if (anno1 != null)
504             for (int i = 0; i < anno1.length; i++)
505                 if (anno1[i].getTypeName().equals(annotationTypeName))
506                     return true;
507 
508         if (anno2 != null)
509             for (int i = 0; i < anno2.length; i++)
510                 if (anno2[i].getTypeName().equals(annotationTypeName))
511                     return true;
512 
513         return false;
514     }
515 
516     public Object getAnnotation(Class clz) throws ClassNotFoundException {
517         ClassFile cf = getClassFile2();
518         AnnotationsAttribute ainfo = (AnnotationsAttribute)
519                 cf.getAttribute(AnnotationsAttribute.invisibleTag);  
520         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
521                 cf.getAttribute(AnnotationsAttribute.visibleTag);  
522         return getAnnotationType(clz, getClassPool(), ainfo, ainfo2);
523     }
524 
525     static Object getAnnotationType(Class clz, ClassPool cp,
526                                     AnnotationsAttribute a1, AnnotationsAttribute a2)
527         throws ClassNotFoundException
528     {
529         Annotation[] anno1, anno2;
530 
531         if (a1 == null)
532             anno1 = null;
533         else
534             anno1 = a1.getAnnotations();
535 
536         if (a2 == null)
537             anno2 = null;
538         else
539             anno2 = a2.getAnnotations();
540 
541         String typeName = clz.getName();
542         if (anno1 != null)
543            for (int i = 0; i < anno1.length; i++)
544               if (anno1[i].getTypeName().equals(typeName))
545                   return toAnnoType(anno1[i], cp);
546 
547         if (anno2 != null)
548            for (int i = 0; i < anno2.length; i++)
549               if (anno2[i].getTypeName().equals(typeName))
550                   return toAnnoType(anno2[i], cp);
551 
552         return null;
553     }
554 
555     public Object[] getAnnotations() throws ClassNotFoundException {
556        return getAnnotations(false);
557     }
558 
559     public Object[] getAvailableAnnotations(){
560        try {
561            return getAnnotations(true);
562        }
563        catch (ClassNotFoundException e) {
564            throw new RuntimeException("Unexpected exception ", e);
565        }
566     }
567 
568     private Object[] getAnnotations(boolean ignoreNotFound)
569         throws ClassNotFoundException
570     {
571         ClassFile cf = getClassFile2();
572         AnnotationsAttribute ainfo = (AnnotationsAttribute)
573                 cf.getAttribute(AnnotationsAttribute.invisibleTag);  
574         AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
575                 cf.getAttribute(AnnotationsAttribute.visibleTag);  
576         return toAnnotationType(ignoreNotFound, getClassPool(), ainfo, ainfo2);
577     }
578 
579     static Object[] toAnnotationType(boolean ignoreNotFound, ClassPool cp,
580                              AnnotationsAttribute a1, AnnotationsAttribute a2)
581         throws ClassNotFoundException
582     {
583         Annotation[] anno1, anno2;
584         int size1, size2;
585 
586         if (a1 == null) {
587             anno1 = null;
588             size1 = 0;
589         }
590         else {
591             anno1 = a1.getAnnotations();
592             size1 = anno1.length;
593         }
594 
595         if (a2 == null) {
596             anno2 = null;
597             size2 = 0;
598         }
599         else {
600             anno2 = a2.getAnnotations();
601             size2 = anno2.length;
602         }
603 
604         if (!ignoreNotFound){
605            Object[] result = new Object[size1 + size2];
606            for (int i = 0; i < size1; i++)
607                result[i] = toAnnoType(anno1[i], cp);
608    
609            for (int j = 0; j < size2; j++)
610                result[j + size1] = toAnnoType(anno2[j], cp);
611    
612            return result;
613         }
614         else{
615            ArrayList annotations = new ArrayList();
616            for (int i = 0 ; i < size1 ; i++){
617               try{
618                  annotations.add(toAnnoType(anno1[i], cp));
619               }
620               catch(ClassNotFoundException e){}
621            }
622            for (int j = 0; j < size2; j++) {
623               try{
624                  annotations.add(toAnnoType(anno2[j], cp));
625               }
626               catch(ClassNotFoundException e){}
627            }
628 
629            return annotations.toArray();
630         }
631     }
632 
633     static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp,
634                                        ParameterAnnotationsAttribute a1,
635                                        ParameterAnnotationsAttribute a2,
636                                        MethodInfo minfo)
637         throws ClassNotFoundException
638     {
639         int numParameters = 0;
640         if (a1 != null) 
641             numParameters = a1.numParameters();
642         else if (a2 != null)
643             numParameters = a2.numParameters();
644         else
645             numParameters = Descriptor.numOfParameters(minfo.getDescriptor());
646 
647         Object[][] result = new Object[numParameters][];
648         for (int i = 0; i < numParameters; i++) {
649             Annotation[] anno1, anno2;
650             int size1, size2;
651 
652             if (a1 == null) {
653                 anno1 = null;
654                 size1 = 0;
655             }
656             else {
657                 anno1 = a1.getAnnotations()[i];
658                 size1 = anno1.length;
659             }
660 
661             if (a2 == null) {
662                 anno2 = null;
663                 size2 = 0;
664             }
665             else {
666                 anno2 = a2.getAnnotations()[i];
667                 size2 = anno2.length;
668             }
669 
670             if (!ignoreNotFound){
671                 result[i] = new Object[size1 + size2];
672                 for (int j = 0; j < size1; ++j)
673                     result[i][j] = toAnnoType(anno1[j], cp);
674    
675                 for (int j = 0; j < size2; ++j)
676                     result[i][j + size1] = toAnnoType(anno2[j], cp);
677             }
678             else{
679                 ArrayList annotations = new ArrayList();
680                 for (int j = 0 ; j < size1 ; j++){
681                     try{
682                         annotations.add(toAnnoType(anno1[j], cp));
683                     }
684                     catch(ClassNotFoundException e){}
685                 }
686                 for (int j = 0; j < size2; j++){
687                     try{
688                         annotations.add(toAnnoType(anno2[j], cp));
689                     }
690                     catch(ClassNotFoundException e){}
691                 }
692 
693                 result[i] = annotations.toArray();
694             }
695         }
696 
697         return result;
698     }
699 
700     private static Object toAnnoType(Annotation anno, ClassPool cp)
701         throws ClassNotFoundException
702     {
703         try {
704             ClassLoader cl = cp.getClassLoader();
705             return anno.toAnnotationType(cl, cp);
706         }
707         catch (ClassNotFoundException e) {
708             ClassLoader cl2 = cp.getClass().getClassLoader();
709             try {
710                 return anno.toAnnotationType(cl2, cp);
711             }
712             catch (ClassNotFoundException e2){
713                 try {
714                     Class clazz = cp.get(anno.getTypeName()).toClass();
715                     return scouter.javassist.bytecode.annotation.AnnotationImpl.make(
716                                             clazz.getClassLoader(),
717                                             clazz, cp, anno);
718                 }
719                 catch (Throwable e3) {
720                     throw new ClassNotFoundException(anno.getTypeName());
721                 }
722             }
723         }
724     }
725 
726     public boolean subclassOf(CtClass superclass) {
727         if (superclass == null)
728             return false;
729 
730         String superName = superclass.getName();
731         CtClass curr = this;
732         try {
733             while (curr != null) {
734                 if (curr.getName().equals(superName))
735                     return true;
736 
737                 curr = curr.getSuperclass();
738             }
739         }
740         catch (Exception ignored) {}
741         return false;
742     }
743 
744     public CtClass getSuperclass() throws NotFoundException {
745         String supername = getClassFile2().getSuperclass();
746         if (supername == null)
747             return null;
748         else
749             return classPool.get(supername);
750     }
751 
752     public void setSuperclass(CtClass clazz) throws CannotCompileException {
753         checkModify();
754         if (isInterface())
755             addInterface(clazz);
756         else
757             getClassFile2().setSuperclass(clazz.getName());
758     }
759 
760     public CtClass[] getInterfaces() throws NotFoundException {
761         String[] ifs = getClassFile2().getInterfaces();
762         int num = ifs.length;
763         CtClass[] ifc = new CtClass[num];
764         for (int i = 0; i < num; ++i)
765             ifc[i] = classPool.get(ifs[i]);
766 
767         return ifc;
768     }
769 
770     public void setInterfaces(CtClass[] list) {
771         checkModify();
772         String[] ifs;
773         if (list == null)
774             ifs = new String[0];
775         else {
776             int num = list.length;
777             ifs = new String[num];
778             for (int i = 0; i < num; ++i)
779                 ifs[i] = list[i].getName();
780         }
781 
782         getClassFile2().setInterfaces(ifs);
783     }
784 
785     public void addInterface(CtClass anInterface) {
786         checkModify();
787         if (anInterface != null)
788             getClassFile2().addInterface(anInterface.getName());
789     }
790 
791     public CtClass getDeclaringClass() throws NotFoundException {
792         ClassFile cf = getClassFile2();
793         InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
794                                                 InnerClassesAttribute.tag);
795         if (ica == null)
796             return null;
797 
798         String name = getName();
799         int n = ica.tableLength();
800         for (int i = 0; i < n; ++i)
801             if (name.equals(ica.innerClass(i))) {
802                 String outName = ica.outerClass(i);
803                 if (outName != null)
804                     return classPool.get(outName);
805                 else {
806                     // maybe anonymous or local class.
807                     EnclosingMethodAttribute ema
808                         = (EnclosingMethodAttribute)cf.getAttribute(
809                                                     EnclosingMethodAttribute.tag);
810                     if (ema != null)
811                         return classPool.get(ema.className());
812                 }
813             }
814 
815         return null;
816     }
817 
818     public CtBehavior getEnclosingBehavior() throws NotFoundException {
819         ClassFile cf = getClassFile2();
820         EnclosingMethodAttribute ema
821                 = (EnclosingMethodAttribute)cf.getAttribute(
822                                                 EnclosingMethodAttribute.tag);
823         if (ema == null)
824             return null;
825         else {
826             CtClass enc = classPool.get(ema.className());
827             String name = ema.methodName();
828             if (MethodInfo.nameInit.equals(name))
829                 return enc.getConstructor(ema.methodDescriptor());
830             else if(MethodInfo.nameClinit.equals(name))
831                 return enc.getClassInitializer();
832             else
833                 return enc.getMethod(name, ema.methodDescriptor());
834         }
835     }
836 
837     public CtClass makeNestedClass(String name, boolean isStatic) {
838         if (!isStatic)
839             throw new RuntimeException(
840                         "sorry, only nested static class is supported");
841 
842         checkModify();
843         CtClass c = classPool.makeNestedClass(getName() + "$" + name);
844         ClassFile cf = getClassFile2();
845         ClassFile cf2 = c.getClassFile2();
846         InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(
847                                                 InnerClassesAttribute.tag);
848         if (ica == null) {
849             ica = new InnerClassesAttribute(cf.getConstPool());
850             cf.addAttribute(ica);
851         }
852 
853         ica.append(c.getName(), this.getName(), name,
854                    (cf2.getAccessFlags() & ~AccessFlag.SUPER) | AccessFlag.STATIC);
855         cf2.addAttribute(ica.copy(cf2.getConstPool(), null));
856         return c;
857     }
858 
859     /* flush cached names.
860      */
861     private void nameReplaced() {
862         CtMember.Cache cache = hasMemberCache();
863         if (cache != null) {
864             CtMember mth = cache.methodHead();
865             CtMember tail = cache.lastMethod();
866             while (mth != tail) {
867                 mth = mth.next();
868                 mth.nameReplaced();
869             }
870         }
871     }
872 
873     /**
874      * Returns null if members are not cached.
875      */
876     protected CtMember.Cache hasMemberCache() {
877         if (memberCache != null)
878             return (CtMember.Cache)memberCache.get();
879         else
880             return null;
881     }
882 
883     protected synchronized CtMember.Cache getMembers() {
884         CtMember.Cache cache = null;
885         if (memberCache == null
886             || (cache = (CtMember.Cache)memberCache.get()) == null) {
887             cache = new CtMember.Cache(this);
888             makeFieldCache(cache);
889             makeBehaviorCache(cache);
890             memberCache = new WeakReference(cache);
891         }
892 
893         return cache;
894     }
895 
896     private void makeFieldCache(CtMember.Cache cache) {
897         List list = getClassFile2().getFields();
898         int n = list.size();
899         for (int i = 0; i < n; ++i) {
900             FieldInfo finfo = (FieldInfo)list.get(i);
901             CtField newField = new CtField(finfo, this);
902             cache.addField(newField);
903         }
904     }
905 
906     private void makeBehaviorCache(CtMember.Cache cache) {
907         List list = getClassFile2().getMethods();
908         int n = list.size();
909         for (int i = 0; i < n; ++i) {
910             MethodInfo minfo = (MethodInfo)list.get(i);
911             if (minfo.isMethod()) {
912                 CtMethod newMethod = new CtMethod(minfo, this);
913                 cache.addMethod(newMethod);
914             }
915             else {
916                 CtConstructor newCons = new CtConstructor(minfo, this);
917                 cache.addConstructor(newCons);
918             }
919         }
920     }
921 
922     public CtField[] getFields() {
923         ArrayList alist = new ArrayList();
924         getFields(alist, this);
925         return (CtField[])alist.toArray(new CtField[alist.size()]);
926     }
927 
928     private static void getFields(ArrayList alist, CtClass cc) {
929         int i, num;
930         if (cc == null)
931             return;
932 
933         try {
934             getFields(alist, cc.getSuperclass());
935         }
936         catch (NotFoundException e) {}
937 
938         try {
939             CtClass[] ifs = cc.getInterfaces();
940             num = ifs.length;
941             for (i = 0; i < num; ++i)
942                 getFields(alist, ifs[i]);
943         }
944         catch (NotFoundException e) {}
945 
946         CtMember.Cache memCache = ((CtClassType)cc).getMembers();
947         CtMember field = memCache.fieldHead();
948         CtMember tail = memCache.lastField();
949         while (field != tail) {
950             field = field.next();
951             if (!Modifier.isPrivate(field.getModifiers()))
952                 alist.add(field);
953         }
954     }
955 
956     public CtField getField(String name, String desc) throws NotFoundException {
957         CtField f = getField2(name, desc);
958         return checkGetField(f, name, desc);
959     }
960 
961     private CtField checkGetField(CtField f, String name, String desc)
962         throws NotFoundException
963     {
964         if (f == null) {
965             String msg = "field: " + name;
966             if (desc != null)
967                 msg += " type " + desc;
968 
969             throw new NotFoundException(msg + " in " + getName());
970         }
971         else
972             return f;
973     }
974 
975     CtField getField2(String name, String desc) {
976         CtField df = getDeclaredField2(name, desc);
977         if (df != null)
978             return df;
979 
980         try {
981             CtClass[] ifs = getInterfaces();
982             int num = ifs.length;
983             for (int i = 0; i < num; ++i) {
984                 CtField f = ifs[i].getField2(name, desc);
985                 if (f != null)
986                     return f;
987             }
988 
989             CtClass s = getSuperclass();
990             if (s != null)
991                 return s.getField2(name, desc);
992         }
993         catch (NotFoundException e) {}
994         return null;
995     }
996 
997     public CtField[] getDeclaredFields() {
998         CtMember.Cache memCache = getMembers();
999         CtMember field = memCache.fieldHead();
1000         CtMember tail = memCache.lastField();
1001         int num = CtMember.Cache.count(field, tail);
1002         CtField[] cfs = new CtField[num];
1003         int i = 0;
1004         while (field != tail) {
1005             field = field.next();
1006             cfs[i++] = (CtField)field;
1007         }
1008 
1009         return cfs;
1010     }
1011 
1012     public CtField getDeclaredField(String name) throws NotFoundException {
1013         return getDeclaredField(name, null);
1014     }
1015 
1016     public CtField getDeclaredField(String name, String desc) throws NotFoundException {
1017         CtField f = getDeclaredField2(name, desc);
1018         return checkGetField(f, name, desc);
1019     }
1020 
1021     private CtField getDeclaredField2(String name, String desc) {
1022         CtMember.Cache memCache = getMembers();
1023         CtMember field = memCache.fieldHead();
1024         CtMember tail = memCache.lastField();
1025         while (field != tail) {
1026             field = field.next();
1027             if (field.getName().equals(name)
1028                 && (desc == null || desc.equals(field.getSignature())))
1029                 return (CtField)field;
1030         }
1031 
1032         return null;
1033     }
1034 
1035     public CtBehavior[] getDeclaredBehaviors() {
1036         CtMember.Cache memCache = getMembers();
1037         CtMember cons = memCache.consHead();
1038         CtMember consTail = memCache.lastCons();
1039         int cnum = CtMember.Cache.count(cons, consTail);
1040         CtMember mth = memCache.methodHead();
1041         CtMember mthTail = memCache.lastMethod();
1042         int mnum = CtMember.Cache.count(mth, mthTail);
1043 
1044         CtBehavior[] cb = new CtBehavior[cnum + mnum];
1045         int i = 0;
1046         while (cons != consTail) {
1047             cons = cons.next();
1048             cb[i++] = (CtBehavior)cons;
1049         }
1050 
1051         while (mth != mthTail) {
1052             mth = mth.next();
1053             cb[i++] = (CtBehavior)mth;
1054         }
1055 
1056         return cb;
1057     }
1058 
1059     public CtConstructor[] getConstructors() {
1060         CtMember.Cache memCache = getMembers();
1061         CtMember cons = memCache.consHead();
1062         CtMember consTail = memCache.lastCons();
1063 
1064         int n = 0;
1065         CtMember mem = cons;
1066         while (mem != consTail) {
1067             mem = mem.next();
1068             if (isPubCons((CtConstructor)mem))
1069                 n++;
1070         }
1071 
1072         CtConstructor[] result = new CtConstructor[n];
1073         int i = 0;
1074         mem = cons;
1075         while (mem != consTail) {
1076             mem = mem.next();
1077             CtConstructor cc = (CtConstructor)mem;
1078             if (isPubCons(cc))
1079                 result[i++] = cc;
1080         }
1081 
1082         return result;
1083     }
1084 
1085     private static boolean isPubCons(CtConstructor cons) {
1086         return !Modifier.isPrivate(cons.getModifiers())
1087                 && cons.isConstructor();
1088     }
1089 
1090     public CtConstructor getConstructor(String desc)
1091         throws NotFoundException
1092     {
1093         CtMember.Cache memCache = getMembers();
1094         CtMember cons = memCache.consHead();
1095         CtMember consTail = memCache.lastCons();
1096 
1097         while (cons != consTail) {
1098             cons = cons.next();
1099             CtConstructor cc = (CtConstructor)cons;
1100             if (cc.getMethodInfo2().getDescriptor().equals(desc)
1101                 && cc.isConstructor())
1102                 return cc;
1103         }
1104 
1105         return super.getConstructor(desc);
1106     }
1107 
1108     public CtConstructor[] getDeclaredConstructors() {
1109         CtMember.Cache memCache = getMembers();
1110         CtMember cons = memCache.consHead();
1111         CtMember consTail = memCache.lastCons();
1112 
1113         int n = 0;
1114         CtMember mem = cons;
1115         while (mem != consTail) {
1116             mem = mem.next();
1117             CtConstructor cc = (CtConstructor)mem;
1118             if (cc.isConstructor())
1119                 n++;
1120         }
1121 
1122         CtConstructor[] result = new CtConstructor[n];
1123         int i = 0;
1124         mem = cons;
1125         while (mem != consTail) {
1126             mem = mem.next();
1127             CtConstructor cc = (CtConstructor)mem;
1128             if (cc.isConstructor())
1129                 result[i++] = cc;
1130         }
1131 
1132         return result;
1133     }
1134 
1135     public CtConstructor getClassInitializer() {
1136         CtMember.Cache memCache = getMembers();
1137         CtMember cons = memCache.consHead();
1138         CtMember consTail = memCache.lastCons();
1139 
1140         while (cons != consTail) {
1141             cons = cons.next();
1142             CtConstructor cc = (CtConstructor)cons;
1143             if (cc.isClassInitializer())
1144                 return cc;
1145         }
1146 
1147         return null;
1148     }
1149 
1150     public CtMethod[] getMethods() {
1151         HashMap h = new HashMap();
1152         getMethods0(h, this);
1153         return (CtMethod[])h.values().toArray(new CtMethod[h.size()]);
1154     }
1155 
1156     private static void getMethods0(HashMap h, CtClass cc) {
1157         try {
1158             CtClass[] ifs = cc.getInterfaces();
1159             int size = ifs.length;
1160             for (int i = 0; i < size; ++i)
1161                 getMethods0(h, ifs[i]);
1162         }
1163         catch (NotFoundException e) {}
1164 
1165         try {
1166             CtClass s = cc.getSuperclass();
1167             if (s != null)
1168                 getMethods0(h, s);
1169         }
1170         catch (NotFoundException e) {}
1171 
1172         if (cc instanceof CtClassType) {
1173             CtMember.Cache memCache = ((CtClassType)cc).getMembers();
1174             CtMember mth = memCache.methodHead();
1175             CtMember mthTail = memCache.lastMethod();
1176 
1177             while (mth != mthTail) {
1178                 mth = mth.next();
1179                 if (!Modifier.isPrivate(mth.getModifiers()))
1180                     h.put(((CtMethod)mth).getStringRep(), mth);
1181             }
1182         }
1183     }
1184 
1185     public CtMethod getMethod(String name, String desc)
1186         throws NotFoundException
1187     {
1188         CtMethod m = getMethod0(this, name, desc);
1189         if (m != null)
1190             return m;
1191         else
1192             throw new NotFoundException(name + "(..) is not found in "
1193                                         + getName());
1194     }
1195 
1196     private static CtMethod getMethod0(CtClass cc,
1197                                        String name, String desc) {
1198         if (cc instanceof CtClassType) {
1199             CtMember.Cache memCache = ((CtClassType)cc).getMembers();
1200             CtMember mth = memCache.methodHead();
1201             CtMember mthTail = memCache.lastMethod();
1202 
1203             while (mth != mthTail) {
1204                 mth = mth.next();
1205                 if (mth.getName().equals(name)
1206                         && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc))
1207                     return (CtMethod)mth;
1208             }
1209         }
1210 
1211         try {
1212             CtClass s = cc.getSuperclass();
1213             if (s != null) {
1214                 CtMethod m = getMethod0(s, name, desc);
1215                 if (m != null)
1216                     return m;
1217             }
1218         }
1219         catch (NotFoundException e) {}
1220 
1221         try {
1222             CtClass[] ifs = cc.getInterfaces();
1223             int size = ifs.length;
1224             for (int i = 0; i < size; ++i) {
1225                 CtMethod m = getMethod0(ifs[i], name, desc);
1226                 if (m != null)
1227                     return m;
1228             }
1229         }
1230         catch (NotFoundException e) {}
1231         return null;
1232     }
1233 
1234     public CtMethod[] getDeclaredMethods() {
1235         CtMember.Cache memCache = getMembers();
1236         CtMember mth = memCache.methodHead();
1237         CtMember mthTail = memCache.lastMethod();
1238         int num = CtMember.Cache.count(mth, mthTail);
1239         CtMethod[] cms = new CtMethod[num];
1240         int i = 0;
1241         while (mth != mthTail) {
1242             mth = mth.next();
1243             cms[i++] = (CtMethod)mth;
1244         }
1245 
1246         return cms;
1247     }
1248 
1249     public CtMethod[] getDeclaredMethods(String name) throws NotFoundException {
1250         CtMember.Cache memCache = getMembers();
1251         CtMember mth = memCache.methodHead();
1252         CtMember mthTail = memCache.lastMethod();
1253         ArrayList methods = new ArrayList();
1254         while (mth != mthTail) {
1255             mth = mth.next();
1256             if (mth.getName().equals(name))
1257                 methods.add((CtMethod)mth);
1258         }
1259 
1260         return (CtMethod[]) methods.toArray(new CtMethod[methods.size()]);
1261     }
1262 
1263     public CtMethod getDeclaredMethod(String name) throws NotFoundException {
1264         CtMember.Cache memCache = getMembers();
1265         CtMember mth = memCache.methodHead();
1266         CtMember mthTail = memCache.lastMethod();
1267         while (mth != mthTail) {
1268             mth = mth.next();
1269             if (mth.getName().equals(name))
1270                 return (CtMethod)mth;
1271         }
1272 
1273         throw new NotFoundException(name + "(..) is not found in "
1274                                     + getName());
1275     }
1276 
1277     public CtMethod getDeclaredMethod(String name, CtClass[] params)
1278         throws NotFoundException
1279     {
1280         String desc = Descriptor.ofParameters(params);
1281         CtMember.Cache memCache = getMembers();
1282         CtMember mth = memCache.methodHead();
1283         CtMember mthTail = memCache.lastMethod();
1284 
1285         while (mth != mthTail) {
1286             mth = mth.next();
1287             if (mth.getName().equals(name)
1288                     && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc))
1289                 return (CtMethod)mth;
1290         }
1291 
1292         throw new NotFoundException(name + "(..) is not found in "
1293                                     + getName());
1294     }
1295 
1296     public void addField(CtField f, String init)
1297         throws CannotCompileException
1298     {
1299         addField(f, CtField.Initializer.byExpr(init));
1300     }
1301 
1302     public void addField(CtField f, CtField.Initializer init)
1303         throws CannotCompileException
1304     {
1305         checkModify();
1306         if (f.getDeclaringClass() != this)
1307             throw new CannotCompileException("cannot add");
1308 
1309         if (init == null)
1310             init = f.getInit();
1311 
1312         if (init != null) {
1313             init.check(f.getSignature());
1314             int mod = f.getModifiers();
1315             if (Modifier.isStatic(mod) && Modifier.isFinal(mod))
1316                 try {
1317                     ConstPool cp = getClassFile2().getConstPool();
1318                     int index = init.getConstantValue(cp, f.getType());
1319                     if (index != 0) {
1320                         f.getFieldInfo2().addAttribute(new ConstantAttribute(cp, index));
1321                         init = null;
1322                     }
1323                 }
1324                 catch (NotFoundException e) {}
1325         }
1326 
1327         getMembers().addField(f);
1328         getClassFile2().addField(f.getFieldInfo2());
1329 
1330         if (init != null) {
1331             FieldInitLink fil = new FieldInitLink(f, init);
1332             FieldInitLink link = fieldInitializers;
1333             if (link == null)
1334                 fieldInitializers = fil;
1335             else {
1336                 while (link.next != null)
1337                     link = link.next;
1338 
1339                 link.next = fil;
1340             }
1341         }
1342     }
1343 
1344     public void removeField(CtField f) throws NotFoundException {
1345         checkModify();
1346         FieldInfo fi = f.getFieldInfo2();
1347         ClassFile cf = getClassFile2();
1348         if (cf.getFields().remove(fi)) {
1349             getMembers().remove(f);
1350             gcConstPool = true;
1351         }
1352         else
1353             throw new NotFoundException(f.toString());
1354     }
1355 
1356     public CtConstructor makeClassInitializer()
1357         throws CannotCompileException
1358     {
1359         CtConstructor clinit = getClassInitializer();
1360         if (clinit != null)
1361             return clinit;
1362 
1363         checkModify();
1364         ClassFile cf = getClassFile2();
1365         Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
1366         modifyClassConstructor(cf, code, 0, 0);
1367         return getClassInitializer();
1368     }
1369 
1370     public void addConstructor(CtConstructor c)
1371         throws CannotCompileException
1372     {
1373         checkModify();
1374         if (c.getDeclaringClass() != this)
1375             throw new CannotCompileException("cannot add");
1376 
1377         getMembers().addConstructor(c);
1378         getClassFile2().addMethod(c.getMethodInfo2());
1379     }
1380 
1381     public void removeConstructor(CtConstructor m) throws NotFoundException {
1382         checkModify();
1383         MethodInfo mi = m.getMethodInfo2();
1384         ClassFile cf = getClassFile2();
1385         if (cf.getMethods().remove(mi)) {
1386             getMembers().remove(m);
1387             gcConstPool = true;
1388         }
1389         else
1390             throw new NotFoundException(m.toString());
1391     }
1392 
1393     public void addMethod(CtMethod m) throws CannotCompileException {
1394         checkModify();
1395         if (m.getDeclaringClass() != this)
1396             throw new CannotCompileException("bad declaring class");
1397 
1398         int mod = m.getModifiers();
1399         if ((getModifiers() & Modifier.INTERFACE) != 0) {
1400             m.setModifiers(mod | Modifier.PUBLIC);
1401             if ((mod & Modifier.ABSTRACT) == 0)
1402                 throw new CannotCompileException(
1403                         "an interface method must be abstract: " + m.toString());
1404         }
1405 
1406         getMembers().addMethod(m);
1407         getClassFile2().addMethod(m.getMethodInfo2());
1408         if ((mod & Modifier.ABSTRACT) != 0)
1409             setModifiers(getModifiers() | Modifier.ABSTRACT);
1410     }
1411 
1412     public void removeMethod(CtMethod m) throws NotFoundException {
1413         checkModify();
1414         MethodInfo mi = m.getMethodInfo2();
1415         ClassFile cf = getClassFile2();
1416         if (cf.getMethods().remove(mi)) {
1417             getMembers().remove(m);
1418             gcConstPool = true;
1419         }
1420         else
1421             throw new NotFoundException(m.toString());
1422     }
1423 
1424     public byte[] getAttribute(String name) {
1425         AttributeInfo ai = getClassFile2().getAttribute(name);
1426         if (ai == null)
1427             return null;
1428         else
1429             return ai.get();
1430     }
1431 
1432     public void setAttribute(String name, byte[] data) {
1433         checkModify();
1434         ClassFile cf = getClassFile2();
1435         cf.addAttribute(new AttributeInfo(cf.getConstPool(), name, data));
1436     }
1437 
1438     public void instrument(CodeConverter converter)
1439         throws CannotCompileException
1440     {
1441         checkModify();
1442         ClassFile cf = getClassFile2();
1443         ConstPool cp = cf.getConstPool();
1444         List list = cf.getMethods();
1445         int n = list.size();
1446         for (int i = 0; i < n; ++i) {
1447             MethodInfo minfo = (MethodInfo)list.get(i);
1448             converter.doit(this, minfo, cp);
1449         }
1450     }
1451 
1452     public void instrument(ExprEditor editor)
1453         throws CannotCompileException
1454     {
1455         checkModify();
1456         ClassFile cf = getClassFile2();
1457         List list = cf.getMethods();
1458         int n = list.size();
1459         for (int i = 0; i < n; ++i) {
1460             MethodInfo minfo = (MethodInfo)list.get(i);
1461             editor.doit(this, minfo);
1462         }
1463     }
1464 
1465     /**
1466      * @see scouter.javassist.CtClass#prune()
1467      * @see scouter.javassist.CtClass#stopPruning(boolean)
1468      */
1469     public void prune() {
1470         if (wasPruned)
1471             return;
1472 
1473         wasPruned = wasFrozen = true;
1474         getClassFile2().prune();
1475     }
1476 
1477     public void rebuildClassFile() { gcConstPool = true; }
1478 
1479     public void toBytecode(DataOutputStream out)
1480         throws CannotCompileException, IOException
1481     {
1482         try {
1483             if (isModified()) {
1484                 checkPruned("toBytecode");
1485                 ClassFile cf = getClassFile2();
1486                 if (gcConstPool) {
1487                     cf.compact();
1488                     gcConstPool = false;
1489                 }
1490 
1491                 modifyClassConstructor(cf);
1492                 modifyConstructors(cf);
1493                 if (debugDump != null)
1494                     dumpClassFile(cf);
1495 
1496                 cf.write(out);
1497                 out.flush();
1498                 fieldInitializers = null;
1499                 if (doPruning) {
1500                     // to save memory
1501                     cf.prune();
1502                     wasPruned = true;
1503                 }
1504             }
1505             else {
1506                 classPool.writeClassfile(getName(), out);
1507                 // to save memory
1508                 // classfile = null;
1509             }
1510 
1511             getCount = 0;
1512             wasFrozen = true;
1513         }
1514         catch (NotFoundException e) {
1515             throw new CannotCompileException(e);
1516         }
1517         catch (IOException e) {
1518             throw new CannotCompileException(e);
1519         }
1520     }
1521 
1522     private void dumpClassFile(ClassFile cf) throws IOException {
1523         DataOutputStream dump = makeFileOutput(debugDump);
1524         try {
1525             cf.write(dump);
1526         }
1527         finally {
1528             dump.close();
1529         }
1530     }
1531 
1532     /* See also checkModified()
1533      */
1534     private void checkPruned(String method) {
1535         if (wasPruned)
1536             throw new RuntimeException(method + "(): " + getName()
1537                                        + " was pruned.");
1538     }
1539 
1540     public boolean stopPruning(boolean stop) {
1541         boolean prev = !doPruning;
1542         doPruning = !stop;
1543         return prev;
1544     }
1545 
1546     private void modifyClassConstructor(ClassFile cf)
1547         throws CannotCompileException, NotFoundException
1548     {
1549         if (fieldInitializers == null)
1550             return;
1551 
1552         Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
1553         Javac jv = new Javac(code, this);
1554         int stacksize = 0;
1555         boolean doInit = false;
1556         for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
1557             CtField f = fi.field;
1558             if (Modifier.isStatic(f.getModifiers())) {
1559                 doInit = true;
1560                 int s = fi.init.compileIfStatic(f.getType(), f.getName(),
1561                                                 code, jv);
1562                 if (stacksize < s)
1563                     stacksize = s;
1564             }
1565         }
1566 
1567         if (doInit)    // need an initializer for static fileds.
1568             modifyClassConstructor(cf, code, stacksize, 0);
1569     }
1570 
1571     private void modifyClassConstructor(ClassFile cf, Bytecode code,
1572                                         int stacksize, int localsize)
1573         throws CannotCompileException
1574     {
1575         MethodInfo m = cf.getStaticInitializer();
1576         if (m == null) {
1577             code.add(Bytecode.RETURN);
1578             code.setMaxStack(stacksize);
1579             code.setMaxLocals(localsize);
1580             m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V");
1581             m.setAccessFlags(AccessFlag.STATIC);
1582             m.setCodeAttribute(code.toCodeAttribute());
1583             cf.addMethod(m);
1584             CtMember.Cache cache = hasMemberCache();
1585             if (cache != null)
1586                 cache.addConstructor(new CtConstructor(m, this));
1587         }
1588         else {
1589             CodeAttribute codeAttr = m.getCodeAttribute();
1590             if (codeAttr == null)
1591                 throw new CannotCompileException("empty <clinit>");
1592 
1593             try {
1594                 CodeIterator it = codeAttr.iterator();
1595                 int pos = it.insertEx(code.get());
1596                 it.insert(code.getExceptionTable(), pos);
1597                 int maxstack = codeAttr.getMaxStack();
1598                 if (maxstack < stacksize)
1599                     codeAttr.setMaxStack(stacksize);
1600 
1601                 int maxlocals = codeAttr.getMaxLocals();
1602                 if (maxlocals < localsize)
1603                     codeAttr.setMaxLocals(localsize);
1604             }
1605             catch (BadBytecode e) {
1606                 throw new CannotCompileException(e);
1607             }
1608         }
1609 
1610         try {
1611             m.rebuildStackMapIf6(classPool, cf);
1612         }
1613         catch (BadBytecode e) {
1614             throw new CannotCompileException(e);
1615         }
1616     }
1617 
1618     private void modifyConstructors(ClassFile cf)
1619         throws CannotCompileException, NotFoundException
1620     {
1621         if (fieldInitializers == null)
1622             return;
1623 
1624         ConstPool cp = cf.getConstPool();
1625         List list = cf.getMethods();
1626         int n = list.size();
1627         for (int i = 0; i < n; ++i) {
1628             MethodInfo minfo = (MethodInfo)list.get(i);
1629             if (minfo.isConstructor()) {
1630                 CodeAttribute codeAttr = minfo.getCodeAttribute();
1631                 if (codeAttr != null)
1632                     try {
1633                         Bytecode init = new Bytecode(cp, 0,
1634                                                 codeAttr.getMaxLocals());
1635                         CtClass[] params
1636                             = Descriptor.getParameterTypes(
1637                                                 minfo.getDescriptor(),
1638                                                 classPool);
1639                         int stacksize = makeFieldInitializer(init, params);
1640                         insertAuxInitializer(codeAttr, init, stacksize);
1641                         minfo.rebuildStackMapIf6(classPool, cf);
1642                     }
1643                     catch (BadBytecode e) {
1644                         throw new CannotCompileException(e);
1645                     }
1646             }
1647         }
1648     }
1649 
1650     private static void insertAuxInitializer(CodeAttribute codeAttr,
1651                                              Bytecode initializer,
1652                                              int stacksize)
1653         throws BadBytecode
1654     {
1655         CodeIterator it = codeAttr.iterator();
1656         int index = it.skipSuperConstructor();
1657         if (index < 0) {
1658             index = it.skipThisConstructor();
1659             if (index >= 0)
1660                 return;         // this() is called.
1661 
1662             // Neither this() or super() is called.
1663         }
1664 
1665         int pos = it.insertEx(initializer.get());
1666         it.insert(initializer.getExceptionTable(), pos);
1667         int maxstack = codeAttr.getMaxStack();
1668         if (maxstack < stacksize)
1669             codeAttr.setMaxStack(stacksize);
1670     }
1671 
1672     private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
1673         throws CannotCompileException, NotFoundException
1674     {
1675         int stacksize = 0;
1676         Javac jv = new Javac(code, this);
1677         try {
1678             jv.recordParams(parameters, false);
1679         }
1680         catch (CompileError e) {
1681             throw new CannotCompileException(e);
1682         }
1683 
1684         for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
1685             CtField f = fi.field;
1686             if (!Modifier.isStatic(f.getModifiers())) {
1687                 int s = fi.init.compile(f.getType(), f.getName(), code,
1688                                         parameters, jv);
1689                 if (stacksize < s)
1690                     stacksize = s;
1691             }
1692         }
1693 
1694         return stacksize;
1695     }
1696 
1697     // Methods used by CtNewWrappedMethod
1698 
1699     Hashtable getHiddenMethods() {
1700         if (hiddenMethods == null)
1701             hiddenMethods = new Hashtable();
1702 
1703         return hiddenMethods;
1704     }
1705 
1706     int getUniqueNumber() { return uniqueNumberSeed++; }
1707 
1708     public String makeUniqueName(String prefix) {
1709         HashMap table = new HashMap();
1710         makeMemberList(table);
1711         Set keys = table.keySet();
1712         String[] methods = new String[keys.size()];
1713         keys.toArray(methods);
1714 
1715         if (notFindInArray(prefix, methods))
1716             return prefix;
1717 
1718         int i = 100;
1719         String name;
1720         do {
1721             if (i > 999)
1722                 throw new RuntimeException("too many unique name");
1723 
1724             name = prefix + i++;
1725         } while (!notFindInArray(name, methods));
1726         return name;
1727     }
1728 
1729     private static boolean notFindInArray(String prefix, String[] values) {
1730         int len = values.length;
1731         for (int i = 0; i < len; i++)
1732             if (values[i].startsWith(prefix))
1733                 return false;
1734 
1735         return true;
1736     }
1737 
1738     private void makeMemberList(HashMap table) {
1739         int mod = getModifiers();
1740         if (Modifier.isAbstract(mod) || Modifier.isInterface(mod))
1741             try {
1742                 CtClass[] ifs = getInterfaces();
1743                 int size = ifs.length;
1744                 for (int i = 0; i < size; i++) {
1745                     CtClass ic =ifs[i];
1746                     if (ic != null && ic instanceof CtClassType)
1747                         ((CtClassType)ic).makeMemberList(table);
1748                 }
1749             }
1750             catch (NotFoundException e) {}
1751 
1752         try {
1753             CtClass s = getSuperclass();
1754             if (s != null && s instanceof CtClassType)
1755                 ((CtClassType)s).makeMemberList(table);
1756         }
1757         catch (NotFoundException e) {}
1758 
1759         List list = getClassFile2().getMethods();
1760         int n = list.size();
1761         for (int i = 0; i < n; i++) {
1762             MethodInfo minfo = (MethodInfo)list.get(i);
1763             table.put(minfo.getName(), this);
1764         }
1765 
1766         list = getClassFile2().getFields();
1767         n = list.size();
1768         for (int i = 0; i < n; i++) {
1769             FieldInfo finfo = (FieldInfo)list.get(i);
1770             table.put(finfo.getName(), this);
1771         }
1772     }
1773 }
1774 
1775 class FieldInitLink {
1776     FieldInitLink next;
1777     CtField field;
1778     CtField.Initializer init;
1779 
1780     FieldInitLink(CtField f, CtField.Initializer i) {
1781         next = null;
1782         field = f;
1783         init = i;
1784     }
1785 }